/** 
    @file frame_listener.cpp 
    @brief Frame Listener class functions
*/
#include "input/frame_listener.hpp"

pm3::FRAME_LISTENER::FRAME_LISTENER() {
	m_time = 0.0;
	m_esc_pressed = false;
    m_num_screenshots = 0;
}

pm3::FRAME_LISTENER::~FRAME_LISTENER() {
    // Clean up the input manager
    if (m_input_manager)
        m_input_manager->cleanup();
}

void pm3::FRAME_LISTENER::init() {
    // Setup input
    m_input_manager = pm3::INPUT_MANAGER::get_singleton_ptr();
    m_input_manager->initialise( pm3::INTERFACE::getSingleton().get_render_window() );
    m_input_manager->add_key_listener( this, "KeyboardListener" );
    m_input_manager->add_mouse_listener( this, "MouseListener" );
}

bool pm3::FRAME_LISTENER::keyPressed( const OIS::KeyEvent &r_event ) {
    LOGGER *log = LOGGER::getSingletonPtr();
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();
    pm3::PIXELWORLD *world = pm3::PIXELWORLD::getSingletonPtr();
    std::ostringstream ss;
    bool god = false;
    static bool pixel = false;

    switch (r_event.key) {
        // Quit
        case OIS::KC_ESCAPE:
            m_esc_pressed = true;
            return true;

        // Toggle god mode
        case OIS::KC_G:
            god = world->m_player.get_god_mode() ^ 1;

            world->m_player.set_god_mode( god );
            if (god)
                world->m_map.get_layer( world->m_player.get_world_pos() )->set_render_mode( pm3::RM_SHOW );
            else
                world->m_map.get_layer( world->m_player.get_world_pos() )->set_render_mode( pm3::RM_PVS );
            break;

        case OIS::KC_P:
            if (!pixel)
                world->m_map.set_pixel( Ogre::Vector3( 2, 0, 8 ), Ogre::ColourValue( 1, 1, 1, 1 ) );
            else
                world->m_map.set_pixel( Ogre::Vector3( 2, 0, 8 ), Ogre::ColourValue( 0, 0, 0, 1 ) );
            pixel ^= 1;
            break;

        // Take a screenshot
        case OIS::KC_SYSRQ:
			ss << "screenshot_" << ++m_num_screenshots << ".png";

            if (intf->get_render_window()) {
    			intf->get_render_window()->writeContentsToFile( ss.str() );

                if (log)
                    log->report( "All", "Screenshot taken\n" );
            }
            break;

        default: break;
    }

	return true;
}

bool pm3::FRAME_LISTENER::keyReleased( const OIS::KeyEvent &r_event ) {
	return true;
}

bool pm3::FRAME_LISTENER::mouseMoved( const OIS::MouseEvent &r_event ) {
    return true;
}

bool pm3::FRAME_LISTENER::mousePressed( const OIS::MouseEvent &r_event, OIS::MouseButtonID i_button ) {
    return false;
}

bool pm3::FRAME_LISTENER::mouseReleased( const OIS::MouseEvent &r_event, OIS::MouseButtonID i_button ) {
    return false;
}

bool pm3::FRAME_LISTENER::frameEnded( const Ogre::FrameEvent &r_event ) {
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();
    float worst = 0, avg = 0, best = 0;
    std::string str;

    if (intf && intf->get_render_window()) {
        // Update FPS on the overlay
        worst = intf->get_render_window()->getWorstFPS();
        avg = intf->get_render_window()->getAverageFPS();
        best = intf->get_render_window()->getBestFPS();

        str = "FPS: " + pm3::util::to_str( worst )  + " .. " +
                        pm3::util::to_str( avg ) + " .. " +
                        pm3::util::to_str( best );
        pm3::hud::TEXT_OVERLAY::getSingleton().set_text( "FPS", str );
    }

	return !m_esc_pressed;
}

bool pm3::FRAME_LISTENER::frameStarted( const Ogre::FrameEvent &r_event ) {
    pm3::PHYSICS *physics = pm3::PHYSICS::getSingletonPtr();
    pm3::PIXELWORLD *world = pm3::PIXELWORLD::getSingletonPtr();

	m_input_manager->capture();

    OIS::Keyboard *keyboard = m_input_manager->get_keyboard();
    OIS::Mouse *mouse = m_input_manager->get_mouse();
    int num_movement_keys = 0;
    Ogre::Degree mouse_sensitivity;

    //! \todo Perhaps there's a better solution for moving the rigid body?

    // Any keyboards attached?
    if (keyboard) {
        // Go forward
        if (keyboard->isKeyDown( OIS::KC_W )) {
            pm3::PIXELWORLD::getSingleton().m_player.move( Ogre::Vector3( 0.0f, 0.0f, -r_event.timeSinceLastFrame ) );
            num_movement_keys++;
        }
        // Go backwards
        if (keyboard->isKeyDown( OIS::KC_S )) {
            pm3::PIXELWORLD::getSingleton().m_player.move( Ogre::Vector3( 0.0f, 0.0f, r_event.timeSinceLastFrame ) );
            num_movement_keys++;
        }
        // Strafe left
        if (keyboard->isKeyDown( OIS::KC_A )) {
            pm3::PIXELWORLD::getSingleton().m_player.move( Ogre::Vector3( -r_event.timeSinceLastFrame, 0.0f, 0.0f ) );
            num_movement_keys++;
        }
        // Strafe right
        if (keyboard->isKeyDown( OIS::KC_D )) {
            pm3::PIXELWORLD::getSingleton().m_player.move( Ogre::Vector3( r_event.timeSinceLastFrame, 0.0f, 0.0f ) );
            num_movement_keys++;
        }
    }

    // No movement? Stop the player then
    if (num_movement_keys == 0)
        pm3::PIXELWORLD::getSingleton().m_player.stop();

    // 's there a mouse?
    if (mouse) {
        const OIS::MouseState &mouse_state = mouse->getMouseState();

        mouse_state.width = Ogre::Root::getSingleton().getAutoCreatedWindow()->getWidth();
        mouse_state.height = Ogre::Root::getSingleton().getAutoCreatedWindow()->getHeight();

        mouse_sensitivity = pm3::PIXELWORLD::getSingleton().m_player.get_mouse_sensitivity();

        pm3::PIXELWORLD::getSingleton().m_player.angle_rotate( -r_event.timeSinceLastFrame * mouse_sensitivity * mouse_state.X.rel, 
                                                               -r_event.timeSinceLastFrame * mouse_sensitivity * mouse_state.Y.rel );
    }

    // Update physics
    if (physics && physics->get_physics_world()) {
        // Update bullet world
        //! \note The third argument is fixed timestep, which should be small enough to allow for enough iterations during one frame
        physics->get_physics_world()->stepSimulation( r_event.timeSinceLastFrame, 10, 1.0f / 200.0f );
        physics->get_physics_world()->debugDrawWorld();

        // Show debug if F3 key is down
        if (keyboard)
            physics->get_physics_debug()->setDebugMode( keyboard->isKeyDown( OIS::KC_F3 ) );

        physics->get_physics_debug()->step();
    }

    // Show portal debug if F4 is down
    if (keyboard)
        pm3::dbg::PORTAL_DRAWER::getSingleton().set_enabled( keyboard->isKeyDown( OIS::KC_F4 ) );

    // Update world
    if (world)
        world->update();

    // Update the camera debug overlay text
    pm3::hud::TEXT_OVERLAY::getSingleton().set_text( "dbgCam", pm3::PIXELWORLD::getSingleton().m_player.get_pos_str() );

	return true;
}

